package study.wechat.applet.client.token;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import study.eggs.utils.JsonUtils;
import study.eggs.utils.RestUtils;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * 获取接口调用凭据
 * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html
 */
@Slf4j
@Component
public class TokenClient {
    private static final String PATH = "/cgi-bin/token";

    private static final String CACHE_KEY = "token";

    @Value("${wechat.applet.host}")
    private String host;
    @Value("${wechat.applet.appId}")
    private String appId;
    @Value("${wechat.applet.appSecret}")
    private String appSecret;

    @Autowired
    private RestTemplate restTemplate;

    private LoadingCache<String, Optional<TokenInfo>> cache = CacheBuilder.newBuilder()
            .expireAfterAccess(1, TimeUnit.HOURS)
            .initialCapacity(500)
            .maximumSize(1000)
            .build(new CacheLoader<String, Optional<TokenInfo>>() {
                @Override
                public Optional<TokenInfo> load(String key) {
                    log.info("loading key= [{}] info", key);
                    TokenInfo tokenInfo = getAccessToken();
                    if (tokenInfo == null) {
                        return Optional.empty();
                    }
                    return Optional.of(tokenInfo);
                }
            });

    @PostConstruct
    public void initCache() {
        TokenInfo info = getAccessToken();
        cache.put(CACHE_KEY, Optional.of(info));
    }

    private TokenInfo getAccessToken() {
        Map<String, Object> param = Maps.newHashMap();
        param.put("appid", appId);
        param.put("secret", appSecret);
        param.put("grant_type", "client_credential");
        log.info(">>>>>> Token Param: {}", JsonUtils.toJson(param));
        //
        String url = RestUtils.buildURL(host, PATH, param);
        TokenInfo info = restTemplate.getForObject(url, TokenInfo.class);
        log.info("<<<<<< Token Result: {}", JsonUtils.toJson(info));
        return info;
    }

    public TokenInfo getTokenInfo() {
        TokenInfo tokenInfo = null;
        try {
            Optional<TokenInfo> op = cache.get(CACHE_KEY);
            if (op.isPresent()) {
                tokenInfo = op.get();
            }
        } catch (Exception ex) {
            throw new RuntimeException("", ex);
        }
        return tokenInfo;
    }

    public String getToken() {
        TokenInfo tokenInfo = getTokenInfo();
        String token = null;
        if (tokenInfo != null) {
            token = tokenInfo.getAccess_token();
        }
        return token;
    }
}
